Práctica 3: Conceptos básicos de sistemas de ficheros
En esta práctica vamos a hacer varios ejercicos orientados a afianzar los conceptos básicos de ficheros y directorios en los sistemas POSIX. Trabajaremos las llamadas al sistema: open, read, write, close, lstat, readlink, symlink, lseek, opendir y readdir.
Se aconseja al alumno que cree un directorio para la práctica con un subdirectorio por ejercico. En las instrucciones se asume que el ejercicio N se hace en un subdirectorio llamado ejercicioN dentro del directorio común de la práctica.
El archivo ficheros_p3.tar.gz contiene una serie de ficheros que pueden ser usados como punto de partida para el desarrollo de los ejercicios de esta práctica, así como unos makefiles que pueden ser usados para la compilación de los distintos proyectos.
Diseña un programa copy.c que permita hacer la copia de un fichero regular usando las llamadas al sistema del estándar POSIX: open, read, write y close. Se deben consultar sus páginas de manual, prestando especial atención a los flags de apertura: O_RDONLY, O_WRONLY, O_CREAT, O_TRUNC.
El programa recibira dos parámetros por la línea de llamadas. El primero será el nombre del fichero a copiar (fichero origen) y el segundo será el nombre que queremos darle a la copia (fichero destino).
El programa debe realizar la copia en bloques de 512B, usando un array local como almacenamiento intermedio entre la lectura y la escritura. El programa debe ir leyendo bloques de 512 bytes del fichero origen y escribiendo los bytes leídos en el fichero destino. Debe tenerse en cuenta que si el tamaño del fichero no es múltiplo de 512 bytes la última vez no se leerán 512 bytes, sino lo que quede hasta el final del fichero (conultar el apartado RETURN VALUE en la página de manual de read). Por ello siempre se deben escribir en el fichero destino tantos bytes como se hayan leído del fichero origen.
Para comprobar el efecto de O_TRUNC, se sugiere al alumno que antes de ejecutar su programa de copia, cree un fichero con cualquier contenido que se llame como el fichero destino. Después puede copiar otro fichero usando el nombre elegido para el fichero destino y comprobar que el contenido anterior desaparece al usarse el flag O_TRUNC.
Para comprobar el funcionamiento correcto de nuestro programa podemos usar los comandos de shell diff y hexdump (este último para ficheros binarios).
Lo primero que vamos a hacer en este ejercicio es crear un enlace simbólico a un fichero cualquiera usando el comando ln. Por ejemplo, si queremos crear un enlace que se llame mylink y que apunte al fichero ../ejercicio1/Makefile usaremos el siguiente comando del shell:
ln
$ ln -s ../ejercicio1/Makefile mylink
Invocando ls -l podremos comprobar que el fichero creado es realmente un enlace simbólico y veremos el fichero apuntado:
ls -l
$ ls -l ... lrwxrwxrwx 1 christian christian 22 Jul 14 13:23 mylink -> ../ejercicio1/Makefile ...
Ahora usaremos nuestro programa de copia para copiar el enlace simbólico. Asumiendo que dicho programa es ../ejercicio1/copy, ejecutamos:
$ ../ejercicio1/copy mylink mylinkcopy
¿Qué tipo de fichero es mylinkcopy? ¿Cuál es el contenido del fichero mylinkcopy? Se pueden usar los comandos ls, stat, cat y diff para obtener las respuestas a estas preguntas.
ls
stat
cat
diff
Es posible que este sea el comportamiento que deseemos, pero también es posible que no. ¿Y si queremos que la copia de un enlace simbólico sea otro enlace simbólico que apunte al mismo fichero que apuntaba el enlace simbólico original?
Vamos a hacer una modificación de nuestro programa de copia del ejercicio anterior, que llamaremos copy2.c. Podemos empezar copiando el programa anterior para luego modificarlo. Haremos entonces una copia usando el comando cp:
cp
$ cp ../ejercicio1/copy.c copy2.c
Después editaremos el fichero copy2.c de modo que: 1. Antes de hacer la copia identifique si el fichero origen es un fichero regular, un enlace simbólico u otro tipo de fichero, haciendo uso de la llamada al sistema lstat (consultar su página de manual).
lstat
Si el fichero origen es un fichero regular, haremos la copia como en el ejercicio anterior.
En cambio, si el fichero origen es un enlace simbólico no tenemos que hacer la copia del fichero apuntado sino crear un enlace simbólico que apunte al mismo fichero al que apunta el fichero origen. Para ello tenemos que seguir los siguientes pasos:
Reservar memoria para hacer una copia de la ruta apuntada. Una llamada a lstat sobre el fichero origen nos permitirá conocer el número de bytes que ocupa el enlace simbólico, que se corresponde con el tamaño de esta ruta sin el carácter null (‘\0’) de final de cadena (consultar la página de manual de lstat). Por tanto sumaremos uno al tamaño obtenido de lstat.
Copiar en este buffer la ruta del fichero apuntado haciendo uso de la llamada al sistema readlink. Deberemos añadir manualmente el caracter null de final de cadena.
readlink
Usar la llamada al sistema symlink para crear un nuevo enlace simbólico que apunte a esta ruta.
symlink
Debéis consultar las páginas de manual de lstat, readlink y symlink.
Si el fichero origen es de cualquier otro tipo (por ejemplo un directorio) mostrarán un mensaje de error y el programa terminará.
En este ejercicio vamos a crear un programa mostrar.c similar al comando cat, que reciba como parámetro el nombre de un fichero y lo muestre por la salida estándar. En este caso asumiremos que es un fichero estándar. Además, nuestro programa recibirá dos argumentos que parsearemos con getopt (consultar su página de manual):
getopt
-n N
-e
El programa debe abrir el fichero indicado en la línea de comandos (consultar optind en la página de manual de getopt) y después situar el marcador de posición en la posición correcta antes de leer. Para ello haremos uso de la llamada al sistema lseek (consultar la página de manual). Si el usuario ha usado el flag -e debemos situar el marcador N bytes antes del final del fichero. Si el usuario ha usado el flag -e debemos avanzar el marcador N bytes desde el comienzo del fichero.
optind
lseek
Una vez situado el marcador de posición, debemos leer leer byte a byte hasta el final de fichero, escribiendo cada byte leído por la salida estándar (descriptor 1).
En este ejercicio vamos a crear un programa espacio.c que reciba una lista de nombres de fichero como parámtros de la llamada, y calculará para cada uno el número total de kilobytes reservados por el sistema para almacenar dicho fichero. En caso de que alguno de los ficheros procesados sean de tipo directorio, se sumarán también los kilobytes ocupados por los ficheros contenidos el directorio (notar que esto es recursivo, porque un directorio puede contener otros directorios).
Para conocer el número de kilobytes reservados por el sistema para almacenar un fichero podemos hacer uso de la llamada a lstat, que nos permite saber el número de bloques de 512 bytes reservados por el sistema.
Para identificar si un fichero es un directorio deberemos hacer una llamada a lstat y consultar el campo st_mode (consultar la página de manual de lstat).
Para recorrer un directorio, primero deberemos abrirlo usando la función de biblioteca opendir y luego leer sus entradas usando la función de biblioteca readdir. Consultar las páginas de manual de estas dos funciones. Notar que las entradas de un directorio no tienen un orden establecido y que todo directorio tiene dos entradas “.” y “..”, que deberemos ignorar si no queremos tener una recursión infinita.
opendir
readdir
El programa debe mostrar por la salida estándar una línea por fichero de la línea de comandos, con el tamaño total en kilobytes del fichero y el nombre de dicho fichero. Para comprobar si nuestro programa funciona correctamente podemos comparar su salida con la del comando du -ks, pasando a este comando la misma lista de ficheros que al nuestro. Notar que se pueden usar los comodines del shell.
du -ks
Ejemplo de uso:
$ ls -l . total 40 -rwxr-xr-x 1 christian christian 20416 Jul 15 12:41 espacio -rw-r--r-- 1 christian christian 1639 Jul 15 12:41 espacio.c -rw-r--r-- 1 christian christian 9056 Jul 15 12:41 espacio.o -rw-r--r-- 1 christian christian 273 Jul 15 09:54 Makefile $ ./espacio . 44K . $ ./espacio * 20K espacio 4K espacio.c 12K espacio.o 4K Makefile
TODO (Fran): Ejercicio nuevo. Script o tareas bash para familiarizarse con otros conceptos o herramientas del sistema relacionados con el sistema de ficheros (permisos de usuario, stat y nodos i, du, df, …)